home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
090
/
byteibm.arc
/
EMSDISK.ASM
< prev
next >
Wrap
Assembly Source File
|
1985-07-12
|
30KB
|
1,117 lines
\foot4
\ctr\- \%page\ -
\right
605057
\left
\lm0
\rm80
\hy
\ssa
\ssb
\bf
\un
name emsdisk
page 55,132
title EMSDISK for Expanded Memory
; EMSDISK for Lotus/Intel/Microsoft Expanded Memory
;
; Copyright (C) 1986 Ray Duncan
; version 1.0 May 1986
;
; This program may be freely reproduced and modified for
; non-commercial (personal) use. It may not be resold or
; incorporated into products for resale without written
; permission from the author.
;
; To convert EMSDISK.ASM into the executable EMSDISK.BIN:
; C>MASM EMSDISK;
; C>LINK EMSDISK;
; C>EXE2BIN EMSDISK.EXE EMSDISK.BIN
; C>DEL EMSDISK.EXE
;
; To link the EMSDISK into the MS-DOS operating system,
; copy the EMSDISK.BIN file to your boot disk and add the line:
;
; DEVICE=EMSDISK.BIN nnnK
;
; into the CONFIG.SYS file on your boot disk (where nnnK is the
; desired EMSDISK size in Kbytes) AFTER the line that loads
; the Expanded Memory Manager (EMM.SYS for Intel Above Board).
;
code segment public 'CODE'
assume cs:code,ds:code,es:code
org 0
cc2_max equ 12 ; max driver command code for DOS 2
cc3_max equ 16 ; max driver command code for DOS 3
cr equ 0dh ; ASCII carriage return
lf equ 0ah ; ASCII line feed
blank equ 020h ; ASCII space code
eom equ '$' ; end of message signal
emm_int equ 67h ; Software Interrupt for communication
; with Expanded Memory Manager
page_size equ 16384 ; bytes per logical EMS page
sec_size equ 512 ; bytes per logical sector, should
; be some multiple of 512 <= 16384.
dir_size equ 256 ; entries in root directory
; logical sectors per EMS page
sec_per_page equ page_size/sec_size
page
; MS-DOS Request Header structure definition
request struc ; request header template structure
; beginning of "Static" portion
rlength db ? ; length of request header
unit db ? ; unit number for this request
command db ? ; request header's command code
status dw ? ; driver's return status word
; bit 15 = Error
; bits 10-14 = Reserved
; bit 9 = Busy
; bit 8 = Done
; bits 0-7 = Error code if bit 15=1
reserve db 8 dup (?) ; reserved area
; end of "Static" portion, the remainder
; is struc. for Read and Write commands
media db ? ; media descriptor byte
address dd ? ; memory address for transfer
count dw ? ; byte/sector count value
sector dw ? ; starting sector value
request ends ; end of request header template
page
;
; Device Driver header
;
header dd -1 ; link to next device driver in chain
dw 0 ; device attribute word
; bit 15 =1 for character devices
; =0 for block devices
; bit 14 =1 if driver can handle IOCTL
; bit 13 =1 if block device & non-IBM format
; bit 12 reserved
; bit 11 =1 if OPEN/CLOSE/RM supported (DOS 3)
; bits 4-10 reserved
; bit 3 =1 if CLOCK device
; bit 2 =1 if NUL device
; bit 1 =1 if Standard Output
; bit 0 =1 if Standard Input
dw strat ; device "Strategy" entry point
dw intr ; device "Interrupt" entry point
db 1 ; number of units, this device
db 7 dup (0) ; reserved area (block dev. drivers)
rh_ptr dd ? ; double word pointer to Request header
save_sp dw 0 ; save DOS's SS:SP
save_ss dw 0
avail_pages dw 0 ; logical EMS pages available
total_pages dw 0 ; total logical EMS pages in system
req_pages dw 0 ; EMSDISK requested size in EMS pages
owned_pages dw 0 ; logical EMS pages owned by EMSDISK
page_frame dw 0 ; segment address of page frame
emm_handle dw 0 ; EMSDISK EMM handle (process id)
dos_ver db 0 ; DOS major version no.
max_cmd dw 0 ; maximum command code, this DOS version
xfer_sec dw 0 ; current sector for transfer
xfer_cnt dw 0 ; sectors successfully transferred
xfer_req dw 0 ; number of sectors requested
xfer_addr dd 0 ; working address for transfer
bpb_array dw bpb ; array of pointers to BPB for each unit
; copy of CONFIG.SYS line for driver
cmd_line db 80 dup (0)
even ; force word alignment
dw 128 dup (0)
stk equ $ ; local stack for use by driver
page
boot_rec equ $
jmp $ ; phony JMP at start of
nop ; boot sector, this field
; must be 3 bytes.
db 'EMRAMDSK' ; OEM identity field
; BIOS Parameter Block (BPB).
; "sectors per cluster", "total
; sectors" & "sectors per FAT"
; are updated by "setup" procedure
bpb dw sec_size ; 0 bytes per sector
sec_clus db 0 ; 2 sectors per cluster
dw 1 ; 3 reserved sectors
db 1 ; 5 number of FATs
dw dir_size ; 6 root directory entries
tot_sec dw 0 ; 8 total sectors
db 0f8h ; 0AH media descriptor
sec_fat dw 0 ; 0BH sectors per FAT
boot_rec_len equ $-boot_rec ; length to copy to
; EMSDISK logical sector 0
page
; EMSDISK Device Driver "Strategy Routine"
;
; Each time a request is made for the logical unit assigned
; to the EMSDISK, MS-DOS first calls the "Strategy Routine",
; then immediately calls the "Interrupt Routine".
;
; The Strategy Routine is passed the address of the
; Request header in ES:BX, which it saves in a local
; variable and then returns to MS-DOS.
strat proc far
; save address of Request header
mov word ptr cs:[rh_ptr],bx
mov word ptr cs:[rh_ptr+2],es
ret ; back to MS-DOS
strat endp
page
; EMSDISK Device Driver "Interrupt Routine"
;
; This entry point is called by MS-DOS immediately after
; the call to the "Strategy Routine", which saved the long
; address of the Request header in the local variable "rh_ptr".
; The "Interrupt Routine" uses the Command Code passed in
; the Request header to transfer to the appropriate device
; handling routine. Each Command Code subroutine returns
; with AX=status.
intr proc far
push ax ; save general registers
push bx
push cx
push dx
push ds
push es
push di
push si
push bp
mov ax,cs ; make local data addressable
mov ds,ax
mov save_ss,ss ; save DOS's stack pointers
mov save_sp,sp
mov ss,ax ; set SS:SP to point to
mov sp,offset stk ; (larger) local stack
les di,[rh_ptr] ; let ES:DI = Request header
; get BX = command Code
mov bl,es:[di.command]
xor bh,bh
cmp bx,max_cmd ; make sure it's legal
jle intr1 ; jump, function code is ok
mov ax,8003h ; set Error bit and "Unknown command" code
jmp intr3
intr1: or bx,bx ; is it init call? (function 0)
jz intr2 ; yes, skip save of context
mov ah,47h ; save EMM page map for
mov dx,emm_handle ; the interrupted process
int 67h
or ah,ah ; jump if EMM error while
jnz intr9 ; saving page mapping context
intr2: shl bx,1 ; form index to dispatch table and
; branch to command code routine
call word ptr [bx+dispatch]
; should return AX = status
les di,[rh_ptr] ; restore ES:DI = addr of Request header
intr3: or ax,0100h ; merge Done bit into status, and
; store into Request header
mov es:[di.status],ax
; Was this initialization call?
mov bl,es:[di.command]
or bl,bl
jz intr4 ; yes, skip restore of context
mov ah,48h ; restore EMM page map
mov dx,emm_handle ; for interrupted process
int 67h
or ah,ah ; jump if EMM error while
jnz intr9 ; restoring page mapping
intr4: ; central exit point from
; driver's "INTR" routine
mov ss,save_ss ; restore DOS's stack
mov sp,save_sp
pop bp ; restore general registers
pop si
pop di
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
ret ; back to MS-DOS
intr9: ; come here if disastrous EMM error
; encountered to set "General Failure"
; error code and return to MS-DOS
les di,[rh_ptr] ; ES:DI = addr of Request header
; Set Error bit, Done bit, and
; Error Code 12 (0CH)
mov es:[di.status],810ch
jmp intr4
intr endp
; MS-DOS Command Codes dispatch table. The "Interrupt" routine uses
; this table and the Command Code supplied in the Request Header to
; transfer to the appropriate driver subroutine. Table entries for
; command codes not supported by this driver point to a dummy routine
; that only sets the "done" status and exits.
dispatch:
dw init ; 0 = initialize driver
dw media_chk ; 1 = media check on block device
dw build_bpb ; 2 = build BIOS parameter block
dw dummy ; 3 = I/O control read
dw read ; 4 = read from device
dw dummy ; 5 = non-destructive read
dw dummy ; 6 = return current input status
dw dummy ; 7 = flush device input buffers
dw write ; 8 = write to device
dw write ; 9 = write with verify
dw dummy ; 10 = return current output status
dw dummy ; 11 = flush output buffers
dw dummy ; 12 = I/O control write
dw dummy ; 13 = device open (DOS 3.X)
dw dummy ; 14 = device close (DOS 3.X)
dw dummy ; 15 = removeable media (DOS 3.X)
dw dummy ; 16 = output until busy (DOS 3.X)
page
;
; Command Code subroutines called by Interrupt Routine
;
; These routines are called with ES:DI = Request Header.
;
; They should return AX = 0 if function was completed
; successfully, or AX = 8000H + Error code if function failed.
;
media_chk proc near ; command code 1 = Media Check
; return "not changed" code
mov byte ptr es:[di+14],1
xor ax,ax ; set "done" status
ret
media_chk endp
build_bpb proc near ; command code 2 = Build BPB
; return BPB address in request header
mov word ptr es:[di+18],offset bpb
mov word ptr es:[di+20],cs
xor ax,ax ; set "done" status
ret
build_bpb endp
read proc near ; command code 4 = Read
call init_xfer ; set up local variables
read1: mov ax,xfer_cnt ; done with all sectors yet?
cmp ax,xfer_req
je read2 ; jump if transfer completed
mov ax,xfer_sec ; get next sector number
call map_sec ; and map it
jc read4 ; jump if mapping error
les di,xfer_addr ; ES:DI = requestor's buffer
mov si,ax ; DS:SI = EMSDISK address
mov ds,page_frame
mov cx,sec_size ; transfer logical sector from
cld ; EMSDISK to requestor
rep movsb
push cs ; restore local addressing
pop ds
inc xfer_sec ; advance sector number
; advance transfer address
add word ptr xfer_addr,sec_size
inc xfer_cnt ; count sectors transferred
jmp read1
read2: ; all sectors successfully
xor ax,ax ; transferred, return ok status
read3: les di,[rh_ptr] ; get address of Request header
mov bx,xfer_cnt ; and poke in actual transfer count
mov es:[di.count],bx ; (in case error aborted transfer
ret ; early)
read4: ; come here if mapping error deteced
mov ax,800bh ; return read fault error code
jmp read3
read endp
write proc near ; command code 8 = write
; command code 9 = write/verify
call init_xfer ; set up local variables
write1: mov ax,xfer_cnt ; done with all sectors yet?
cmp ax,xfer_req
je write2 ; jump if transfer completed
mov ax,xfer_sec ; get next sector number
call map_sec ; and map it
jc write4 ; jump if mapping error detected
mov di,ax ; ES:DI = EMSDISK address
mov es,page_frame
lds si,xfer_addr ; DS:SI = requestor's buffer
mov cx,sec_size ; transfer logical sector from
cld ; requestor to EMSDISK
rep movsb
push cs ; restore local addressing
pop ds
inc xfer_sec ; advance sector number
; advance transfer address
add word ptr xfer_addr,sec_size
inc xfer_cnt ; count sectors transferred
jmp write1
write2: ; all sectors successfully
xor ax,ax ; transferred, return ok status
write3:
les di,[rh_ptr] ; get address of Request header
mov bx,xfer_cnt ; and poke in actual transfer count
mov es:[di.count],bx ; (in case error aborted transfer
ret ; early)
write4: ; error detected
mov ax,800ah ; return write fault error code
jmp write3
write endp
dummy proc near ; this command code routine is
; called for functions that are
; not supported or are applicable
; to character devices only.
xor ax,ax ; return success flag for all
ret
dummy endp
page
;
; Map into memory a logical "disk" sector from the EMS
; pages allocated to the EMSDISK.
;
; Called with: AX = logical sector number
;
; Returns: CY = clear if no error
; AX = offset within EMS page frame
; AX,CX,DX destroyed
;
; CY = set if EMM mapping error
; AX,CX,DX destroyed
;
map_sec proc near
mov dx,0 ; divide sector no. by sectors
mov cx,sec_per_page ; per page, to get EMS page number
div cx ; now AX=EMS page,DX=rel. sector
push dx ; save remainder
mov bx,ax ; BX <- EMS page number
mov ax,4400h ; map function, phys. page=0
mov dx,emm_handle ; process ID for EMSDISK
int 67h
or ah,ah ; if EMM error, jump to return flag
jnz map_sec1
pop ax ; get remainder (relative sector)
mov cx,sec_size ; remainder*sec_size to get offset
mul cx ; into EMS logical page
clc ; return CY=clear for no error
ret ; back to caller
map_sec1: ; come here if EMM mapping error
add sp,2 ; clear stack
stc ; return CY=set for error
ret
map_sec endp
;
; Set up to perform Read or Write subfunction by copying
; requestor's buffer address, starting sector, and sector
; count out of Request header into local variables.
;
init_xfer proc near ; call ES:DI=request header
; extracts addr, start, count
; to working variables
push es ; save Request header addr
push di
; initialize working variables
; for transfer
mov ax,es:[di.sector]
mov xfer_sec,ax ; starting sector number
mov ax,es:[di.count]
mov xfer_req,ax ; sectors requested
les di,es:[di.address]
; requestor's buffer offset
mov word ptr xfer_addr,di
; requestor's buffer segment
mov word ptr xfer_addr+2,es
mov xfer_cnt,0 ; init sectors transferred count
pop di ; restore Request header addr
pop es
ret
init_xfer endp
page
; The Initialization code for the driver is called only
; once when the driver is loaded. For a block device, it
; must return the number of units, address of an array of
; pointers to BPBs for each unit, and the address of the
; first free memory above the driver in the Request Header.
; Only MS-DOS services 01-0CH and 30H can be called by the
; Initialization function.
;
; "Init" returns its own address to the DOS as the start of
; free memory after the driver, so that the memory occupied
; occupied by "init" and its subroutines will be reclaimed.
;
init proc near ; command code 0 = Initialize Driver
; on entry ES:DI = request header
les di,es:[di+18] ; get addr of CONFIG.SYS line
; and copy it to local buffer
mov si,offset cmd_line
mov cx,80 ; CX = max line length to copy
init0: mov al,es:[di] ; get next character of line
cmp al,cr ; found Carriage Return yet?
je init1 ; yes, end of line
mov [si],al ; no, copy this character
inc di ; bump string pointers
inc si
loop init0 ; loop unless 80 characters copied
init1: mov ah,30h ; get DOS version
int 21h
mov dos_ver,al ; and major version no. for later
; set maximum legal command code
mov max_cmd,cc2_max ; assume DOS 2.X
cmp al,2
je init2 ; jump if DOS 2
mov max_cmd,cc3_max ; was DOS 3+, set higher value
init2: xor ax,ax ; check if EMM driver present
mov es,ax ; if EMM is present, address in
mov bx,emm_int*4 ; vector points to EMM driver.
mov es,es:[bx+2] ; now ES:0000 would point to EMM header
mov di,10 ; let ES:DI = addr of device name field
; let DS:SI = addr of EMM driver name
mov si,offset emm_name
mov cx,8 ; length of device name field
cld
repz cmpsb ; compare EMM name to driver header.
jz init3 ; jump if strings matched.
mov dx,offset msg1 ; if strings didn't match,
jmp init_err ; driver is absent, exit.
init3: mov ah,40h ; EMM driver module is present,
int 67h ; test EM Manager status.
or ah,ah
jz init4 ; jump, driver is OK
mov dx,offset msg2 ; EMM is non-functional, print
jmp init_err ; error message and exit
init4: mov ah,46h ; check EM Manager version
int 67h
or ah,ah
jz init5 ; jump, got version ok
init45: mov dx,offset msg3 ; print EMM error message
jmp init_err ; and exit, discarding driver
init5: cmp al,030h ; make sure at least ver. 3.0
jae init6 ; jump if EMM version adequate
mov dx,offset msg6
jmp init_err ; EMM version too early, exit
init6: mov ah,41h ; get page frame segment
int 67h
or ah,ah
jnz init45 ; jump if failed to get frame
mov page_frame,bx ; save segment of page frame
mov ah,42h ; get number of available pages
int 67h
or ah,ah
jnz init45 ; jump if error on get pages
mov total_pages,dx ; save total EMM pages
mov avail_pages,bx ; save available EMM pages
or bx,bx
jnz init7 ; proceed if some pages available
mov dx,offset msg4
jmp init_err ; no EMS pages left, exit
init7: call get_kb ; convert desired size of EMSDISK
mov ah,43h ; try and allocate EMM pages
mov bx,owned_pages
int 67h ; if allocation is successful
or ah,ah
jz init8 ; jump, allocation was successful
mov dx,offset msg5 ; if allocation failed, print
jmp init_err ; error message and exit
init8: mov emm_handle,dx ; save EMM handle for this driver
call setup ; set up Bios Parameter Block
call format ; format the EMSDISK
jnc init9 ; jump if no error during format
mov dx,offset msg7 ; formatting error, exit
jmp init_err
init9: call signon ; format and display driver ident.
les di,cs:[rh_ptr] ; restore ES:DI=Request header
; return first usable memory addr.
; ("break address") above driver
mov word ptr es:[di.address],offset init
mov word ptr es:[di.address+2],cs
; return EMSDISK logical units = 1
mov byte ptr es:[di+13],1
; return address of BPB array
mov word ptr es:[di+18],offset bpb_array
mov word ptr es:[di+20],cs
xor ax,ax ; return success status
ret
init_err: ; EMM initialization failed,
; print error message and
; discard EMSDISK driver.
push dx ; save specific error message
mov dx,offset ermsg ; print error heading
mov ah,9
int 21h
pop dx ; now print error description
mov ah,9
int 21h
les di,cs:[rh_ptr] ; restore ES:DI=Request header
; discard this driver...
; set break addr=start of driver
mov word ptr es:[di.address],0
mov word ptr es:[di.address+2],cs
; set number of logical units=0
mov byte ptr es:[di+13],0
xor ax,ax ; return status
ret
init endp
setup proc near ; calculate BPB fields depending
; on number of sectors in EMSDISK
mov ax,owned_pages ; find total sectors in EMSDISK,
mov cx,sec_per_page ; update BIOS parameter block
mul cx
mov tot_sec,ax ; store into (word ptr bpb+8)
; determine number of sectors
; per cluster (allocation unit).
; must have tot. clusters <4087
; so can use 12-bit FAT fields.
mov cx,2 ; start with 2 sectors/cluster
setup1: mov ax,tot_sec ; try this cluster size...
mov dx,0 ; divide total sectors by
div cx ; sectors per cluster.
cmp ax,4086 ; resulting clusters < 4087?
jna setup2 ; yes, use it
shl cx,1 ; sec/cluster*2, try again
jmp setup1
setup2: mov sec_clus,cl ; update sectors per cluster
; in Bios Parameter Block
; now AX = total clusters in disk
mov dx,ax ; clusters*1.5 = FAT bytes needed
add ax,ax ; (* 2)
add ax,dx ; (* 3)
shr ax,1 ; (/ 2)
mov dx,0 ; FAT bytes needed / bytes/sector
mov cx,sec_size ; = number of FAT sectors needed
div cx
or dx,dx ; any remainder?
jz setup3 ; no,jump
inc ax ; round up to next sector
setup3: mov sec_fat,ax ; store number of FAT sectors
ret ; into (word ptr bpb+0bh)
setup endp
format proc near ; format the EMSDISK area
; returns CY = clear if successful
; CY = set if error
mov bx,0 ; first clear EMSDISK area
fmt1: cmp bx,owned_pages ; done with all EMS pages?
je fmt2 ; yes, jump
push bx ; save current page number
mov ax,4400h ; map to physical page 0
mov dx,emm_handle ; get our process id
int 67h ; request mapping by EMM
pop bx ; restore page number
or ah,ah ; if bad mapping give up
jnz fmt9 ; (should never happen)
mov es,page_frame ; set ES:DI = EMS page
xor di,di
mov cx,page_size ; page length
xor al,al ; fill page with zeros
cld
rep stosb
inc bx ; increment page and loop
jmp fmt1
fmt2: ; copy phony boot sector with
; Bios Parameter Block to
; EMSDISK's logical sector 0
mov ax,0 ; map in logical sector 0
call map_sec
jc fmt9 ; jump if mapping error
mov di,ax ; let ES:DI = point to sector 0
mov es,page_frame
; let DS:SI point to boot rec.
mov si,offset boot_rec
mov cx,boot_rec_len ; CX = length to copy
rep movsb ; now transfer boot sector
mov ax,1 ; map in EMSDISK logical
call map_sec ; sector 1 (first sec of FAT)
jc fmt9 ; jump if mapping error
mov di,ax
mov es,page_frame ; ES:DI points to sector 1
; put media descriptor into FAT
; byte 0, bytes 1-2 must be -1
mov al,byte ptr [bpb+0ah]
mov es:[di],al
mov word ptr es:[di+1],-1
; now set up EMSDISK volume label.
; first directory sector=
; number of FATs*length of FAT
; plus no. of reserved sectors
mov al,byte ptr [bpb+5]
xor ah,ah
mul word ptr [bpb+0bh]
add ax,word ptr [bpb+3]
call map_sec ; map in first directory block
jc fmt9 ; jump if mapping error
mov di,ax ; copy volume label to directory
mov es,page_frame
mov si,offset vol_name
mov cx,vol_name_len
rep movsb
clc ; CY = clear, format successful
ret
fmt9: stc ; CY = set if error during format
ret
format endp
get_kb proc near ; get desired size of EMSDISK
; in Kbytes from CONFIG.SYS line,
; sets "req_pages" and "owned_pages".
; if no disk size requested by user,
; makes largest possible EMSDISK.
mov si,offset cmd_line
getkb1: lodsb ; scan for end of driver name
or al,al ; if zero, no Kbytes requested
jz getkb9
cmp al,blank ; if blank, reached end of name
jne getkb1
getkb2: lodsb ; scan for start of Kbytes field
or al,al ; if zero found, no Kbytes requested
jz getkb9
cmp al,blank ; if blank, keep searching
je getkb2
dec si ; point to start of field and
call ascbin ; convert string to binary Kbytes
or ax,ax ; if request=0, make big disk
jz getkb9
mov dx,ax ; save copy of Kbytes
mov cx,4 ; divide Kbytes by 16 to get
shr ax,cl ; requested EMS pages
and dx,0fh ; round up needed?
jz getkb3 ; jump if multiple of 16 Kbytes
inc ax
getkb3: mov req_pages,ax ; save requested EMS pages
cmp ax,avail_pages ; compare with pages available
jna getkb4 ; jump if ok
mov ax,avail_pages ; request too large, use avail. only
getkb4: mov owned_pages,ax ; set total EMS pages to allocate
ret
getkb9: mov ax,avail_pages ; no size requested by user, set
mov req_pages,ax ; requested and owned to maximum possible.
mov owned_pages,ax
ret
get_kb endp
signon proc near ; format and print driver ident.
les di,[rh_ptr] ; let ES:DI = Request Header.
mov al,es:[di+22] ; get drive code from header,
add al,'A' ; convert it to ASCII, and
mov drv_code,al ; store into sign-on message
mov ax,cs ; convert load address to ASCII
mov bx,offset dh_addr
call hexasc
mov ax,total_pages ; format Kbytes of EM installed
mov dx,16
mul dx ; pages * 16 = Kbytes
mov cx,10
mov si,offset kb_installed+3
call binasc ; convert Kbytes to ASCII
mov ax,avail_pages ; format Kbytes of EM available
mov dx,16
mul dx ; pages * 16 = Kbytes
mov cx,10
mov si,offset kb_avail+3
call binasc ; convert Kbytes to ASCII
mov ax,owned_pages ; format Kbytes assigned to EMSDISK
mov dx,16
mul dx ; pages * 16 = Kbytes
mov cx,10
mov si,offset kb_assigned+3
call binasc ; convert Kbytes to ASCII
mov ah,9 ; print sign-on message
mov dx,offset ident ; and copyright notice
int 21h
mov dx,offset dos2m ; check DOS version, if
cmp dos_ver,2 ; DOS 2 can't know drive letter
je signon1
mov dx,offset dos3m ; if DOS 3 can display drive
signon1:
mov ah,9 ; print load address, bytes
int 21h ; in EMSDISK, etc.
ret ; back to caller
signon endp
ident db cr,lf,lf
db 'Expanded Memory EMSDISK 1.0'
db cr,lf
db 'Copyright (C) 1986 Ray Duncan'
db cr,lf,lf,eom
dos3m db 'EMSDISK will be drive '
drv_code db 'X:'
db cr,lf
dos2m db 'Device driver loaded at '
dh_addr db 'XXXX:0000'
db cr,lf,lf
kb_installed db ' Kbytes Expanded Memory installed.'
db cr,lf
kb_avail db ' Kbytes Expanded Memory available.'
db cr,lf
kb_assigned db ' Kbytes assigned to EMSDISK.'
db cr,lf,eom
emm_name db 'EMMXXXX0',0 ; device name for Expanded
; Memory Manager
ermsg db cr,lf
db 'EMS RAMDISK installation error:'
db cr,lf,eom
msg1 db 'Expanded Memory Manager not found.'
db cr,lf,eom
msg2 db 'Expanded Memory not functional.'
db cr,lf,eom
msg3 db 'Expanded Memory Manager error.'
db cr,lf,eom
msg4 db 'No Expanded Memory pages available.'
db cr,lf,eom
msg5 db 'Expanded Memory allocation failed.'
db cr,lf,eom
msg6 db 'Wrong Expanded Memory Manager version.'
db cr,lf,eom
msg7 db 'Unable to format EMSDISK.'
db cr,lf,eom
; phony volume label, copied to
; EMSDISK's first directory sector
vol_name db 'EMS_RAMDISK'
db 08h ; volume label attribute byte
db 10 dup (0)
dw 0 ; time
dw 0cb0h ; date = May 16, 1986
db 6 dup (0)
vol_name_len equ $-vol_name
page
; HEXASC: convert a binary 16-bit number into
; a "hexadecimal" ASCII string.
;
; Call with AX = value to convert
; DS:BX = address to store 4-character string
;
; Returns AX, BX destroyed, other registers preserved
;
hexasc proc near
push cx ; save registers
push dx
mov dx,4 ; initialize character counter
hexasc1:
mov cx,4 ; isolate next four bits
rol ax,cl
mov cx,ax
and cx,0fh
add cx,'0' ; convert to ASCII
cmp cx,'9' ; is it 0-9?
jbe hexasc2 ; yes, jump
add cx,'A'-'9'-1 ; add fudge factor for A-F
hexasc2: ; store this character
mov [bx],cl
inc bx ; bump string pointer
dec dx ; count characters converted
jnz hexasc1 ; loop, not four yet
pop dx ; restore registers
pop cx
ret ; back to caller
hexasc endp
;
; BINASC: Convert 32 bit binary value to ASCII string.
;
; Call with DX:AX = signed 32 bit value
; CX = radix
; SI = last byte of area to store resulting string
; (make sure enough room is available to store
; the string in the radix you have selected.)
;
; Destroys AX, BX, CX, DX, and SI.
;
binasc proc near ; convert DX:AX to ASCII.
; force storage of at least 1 digit.
mov byte ptr [si],'0'
or dx,dx ; test sign of 32 bit value,
pushf ; and save sign on stack.
jns bin1 ; jump if it was positive.
not dx ; it was negative, take 2's complement
not ax ; of the value.
add ax,1
adc dx,0
bin1: ; divide the 32 bit value by the radix
; to extract the next digit for the
; forming string.
mov bx,ax ; is the value zero yet?
or bx,dx
jz bin3 ; yes, we are done converting.
call divide ; no, divide by radix.
add bl,'0' ; convert the remainder to an ASCII digit.
cmp bl,'9' ; we might be converting to hex ASCII,
jle bin2 ; jump if in range 0-9,
add bl,'A'-'9'-1 ; correct it if in range A-F.
bin2: mov [si],bl ; store this character into string.
dec si ; back up through string,
jmp bin1 ; and do it again.
bin3: ; restore sign flag,
popf ; was original value negative?
jns bin4 ; no, jump
; yes,store sign into output string.
mov byte ptr [si],'-'
bin4: ret ; back to caller.
binasc endp
;
; General purpose 32 bit by 16 bit unsigned divide.
; This must be used instead of the plain machine unsigned divide
; for cases where the quotient may overflow 16 bits (for example,
; dividing 100,000 by 2). If called with a zero divisor, this
; routine returns the dividend unchanged and gives no warning.
;
; Call with DX:AX = 32 bit dividend
; CX = divisor
;
; Returns DX:AX = quotient
; BX = remainder
; CX = divisor (unchanged)
;
divide proc near ; Divide DX:AX by CX
jcxz div1 ; exit if divide by zero
push ax ; 0:dividend_upper/divisor
mov ax,dx
xor dx,dx
div cx
mov bx,ax ; BX = quotient1
pop ax ; remainder1:dividend_lower/divisor
div cx
xchg bx,dx ; DX:AX = quotient1:quotient2
div1: ret ; BX = remainder2
divide endp
;
; ASCBIN: Convert decimal ASCII string to unsigned binary integer.
; Conversion ends on first byte not {'0'...'9'}.
;
; Call with DS:SI = addr of ASCII string
;
; Returns AX = unsigned binary integer
; DS:SI points to unconvertable character + 1
;
ascbin proc near
push dx ; save prev. register contents
xor dx,dx ; set forming answer to zero
ascbin1:
lodsb ; get next character from input string.
cmp al,'9' ; make sure it is legal character 0-9.
ja ascbin2 ; char > '9', exit with error flag.
cmp al,'0'
jb ascbin2 ; char < '0', exit with error flag.
push ax ; save character from string
mov ax,10 ; multiply prev answer * 10
mul dx ; low part of answer*10 now in AX
pop dx
and dx,0fh ; isolate binary value 0-9
add dx,ax ; accumulate forming answer
jmp ascbin1 ; get next character
ascbin2: ; end of conversion,
mov ax,dx ; return answer in AX
pop dx ; and restore register DX.
ret
ascbin endp
code ends
end
ersion,
mov ax,dx ; return answer in AX
pop dx ; and restore register DX.
ret
asc